/**
 * 文档验证服务
 * 提供文档语法检查和诊断功能
 */
import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver/node'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { checkKeywordCase, checkStatementEnd, DIAGNOSTIC_MESSAGES } from '../core'
import { createDiagnostic, REGEX_PATTERNS } from '../utils'

// 去掉字符串与注释，返回只含代码的上半部分
const stripStringsAndComments = (line: string): string => {
	// 去掉字符串
	let code = line.replace(/'[^']*'|"[^"]*"/g, '')
	// 去掉注释（# 或 -- 之后的内容）
	const hashIdx = code.indexOf('#')
	const dashIdx = code.indexOf('--')
	let cut = code.length
	if (hashIdx >= 0) cut = Math.min(cut, hashIdx)
	if (dashIdx >= 0) cut = Math.min(cut, dashIdx)
	return code.slice(0, cut)
}

/**
 * 跨行字符串感知的代码预处理：
 * - 去掉字符串内容（保留为空），支持 '...'、"..."，以及成对引号转义（'' 或 ""）
 * - 仅在非字符串状态下识别行内注释：# 或 --（从出现处到行尾）
 * - 返回与原行数一致的“代码-only”数组，供结构化语法检查使用
 */
const preprocessLinesForStructure = (lines: string[]): string[] => {
	let inString: null | "'" | '"' = null
	const out: string[] = []
	for (let i = 0; i < lines.length; i++) {
		const line = lines[i]
		let buf = ''
		for (let p = 0; p < line.length; p++) {
			const ch = line[p]

			if (inString) {
				// 处理成对引号作为转义：'' 或 ""
				if (ch === inString) {
					const next = line[p + 1]
					if (next === inString) {
						// 转义的引号对，消耗但不输出
						p++ // 跳过成对引号
						continue
					} else {
						// 结束字符串
						inString = null
						continue
					}
				} else {
					// 字符串内字符全部丢弃（不输出）
					continue
				}
			} else {
				// 非字符串状态：检测注释开始
				if (ch === '#') {
					// 行余下为注释
					break
				}
				// -- 注释（需保证后面有一个 -）
				if (ch === '-' && line[p + 1] === '-') {
					break
				}
				// 检测字符串开始
				if (ch === '\'' || ch === '"') {
					inString = ch as "'" | '"'
					continue
				}
				// 正常代码字符
				buf += ch
			}
		}
		out.push(buf)
	}
	return out
}

// 基础结构化语法检查（整文件一次性扫描）
const checkStructureDiagnostics = (document: TextDocument, lines: string[]) => {
	type BlockType = 'FUNCTION' | 'IF' | 'FOR' | 'WHILE' | 'CASE' | 'FOREACH'
	const diagnostics: Diagnostic[] = []
	const stack: { type: BlockType; line: number }[] = []
	// 预处理：得到去除字符串与注释的“代码-only”行
	const codeOnly = preprocessLinesForStructure(lines)

	const push = (type: BlockType, line: number) => stack.push({ type, line })
	const popExpect = (type: BlockType, closeLine: number, tokenText: string) => {
		if (stack.length === 0) {
			const range = {
				start: { line: closeLine, character: 0 },
				end: { line: closeLine, character: tokenText.length }
			}
			diagnostics.push(createDiagnostic(document, DiagnosticSeverity.Error, range, `语法错误：在没有开始块的情况下出现 ${tokenText}`))
			return
		}
		const top = stack[stack.length - 1]
		if (top.type !== type) {
			const range = {
				start: { line: closeLine, character: 0 },
				end: { line: closeLine, character: tokenText.length }
			}
			diagnostics.push(createDiagnostic(document, DiagnosticSeverity.Error, range, `语法错误：期望结束 ${top.type}，但遇到 ${tokenText}`))
		} else {
			stack.pop()
		}
	}

	for (let i = 0; i < lines.length; i++) {
		const code = codeOnly[i] ?? ''
		const upper = code.toUpperCase()

		// 忽略空行
		if (!upper.trim()) continue

		// IF 必须包含 THEN（支持多行条件与换行 THEN）
		if (/^\s*IF\b/i.test(code)) {
			let hasThenHere = /\bTHEN\b/i.test(code)
			if (!hasThenHere) {
				// 在后续若干行内查找 THEN，允许续行以 AND/OR/NOT 开头或任意非块起始的普通行
				let foundThenAhead = false
				for (let j = i + 1; j < Math.min(lines.length, i + 8); j++) {
					const ahead = codeOnly[j] ?? ''
					const aheadUpper = ahead.toUpperCase().trimStart()
					// 若下一行就是 THEN（或包含 THEN）则接受
					if (/^\s*THEN\b/i.test(ahead) || /\bTHEN\b/i.test(ahead)) {
						foundThenAhead = true
						break
					}
					// 若遇到明显新的块开始或块结束，停止前瞻
					if (/^(END\b|END\s+(FUNCTION|IF|FOR|WHILE|CASE|FOREACH)\b|(?:PRIVATE\s+)?FUNCTION\b|IF\b|FOR\b|WHILE\b|CASE\b|FOREACH\b)/i.test(aheadUpper)) {
						break
					}
					// 否则继续向下看几行（例如以 AND/OR/NOT 开头的续行）
				}
				if (!foundThenAhead) {
					const character = Math.max(0, upper.indexOf('IF'))
					const range = { start: { line: i, character }, end: { line: i, character: character + 2 } }
					diagnostics.push(createDiagnostic(document, DiagnosticSeverity.Error, range, '语法错误：IF 缺少 THEN'))
				}
			}
		}

		// ELSE / ELSE IF 必须在 IF 块内部
		if (/\bELSE(\s+IF\b)?/i.test(code)) {
			const hasIf = stack.some(s => s.type === 'IF')
			if (!hasIf) {
				const ch = Math.max(0, upper.indexOf('ELSE'))
				const len = upper.includes('ELSE IF') ? 'ELSE IF'.length : 'ELSE'.length
				const range = { start: { line: i, character: ch }, end: { line: i, character: ch + len } }
				diagnostics.push(createDiagnostic(document, DiagnosticSeverity.Error, range, '语法错误：ELSE/ELSE IF 必须在 IF 块内'))
			}
		}

		// PREPARE name FROM ...
		if (/^\s*PREPARE\b/i.test(code)) {
			if (!/^\s*PREPARE\s+[A-Z_]\w*\s+FROM\b/i.test(code)) {
				const ch = Math.max(0, upper.indexOf('PREPARE'))
				const range = { start: { line: i, character: ch }, end: { line: i, character: ch + 'PREPARE'.length } }
				diagnostics.push(createDiagnostic(document, DiagnosticSeverity.Error, range, '语法错误：PREPARE 语句应为 "PREPARE name FROM 表达式"'))
			}
		}

		// DECLARE name CURSOR FOR ... 或 FROM ...
		if (/^\s*DECLARE\b/i.test(code)) {
			const okFor = /^\s*DECLARE\s+[A-Z_]\w*\s+CURSOR\s+FOR\b/i.test(code)
			const okFrom = /^\s*DECLARE\s+[A-Z_]\w*\s+CURSOR\s+FROM\b/i.test(code)
			if (!okFor && !okFrom) {
				const ch = Math.max(0, upper.indexOf('DECLARE'))
				const range = { start: { line: i, character: ch }, end: { line: i, character: ch + 'DECLARE'.length } }
				diagnostics.push(createDiagnostic(document, DiagnosticSeverity.Error, range, '语法错误：DECLARE 语句应为 "DECLARE name CURSOR FOR/FROM …"'))
			}
		}

		// 块开始
		if (/^\s*(?:PRIVATE\s+)?FUNCTION\b/i.test(code)) push('FUNCTION', i)
		else if (/^\s*IF\b/i.test(code)) push('IF', i)
		else if (/^\s*FOR\b/i.test(code)) push('FOR', i)
		else if (/^\s*WHILE\b/i.test(code)) push('WHILE', i)
		else if (/^\s*CASE\b/i.test(code)) push('CASE', i)
		else if (/^\s*FOREACH\b/i.test(code)) push('FOREACH', i)

		// 块结束
		if (/^\s*END\s+FUNCTION\b/i.test(code)) popExpect('FUNCTION', i, 'END FUNCTION')
		else if (/^\s*END\s+IF\b/i.test(code)) popExpect('IF', i, 'END IF')
		else if (/^\s*END\s+FOR\b/i.test(code)) popExpect('FOR', i, 'END FOR')
		else if (/^\s*END\s+WHILE\b/i.test(code)) popExpect('WHILE', i, 'END WHILE')
		else if (/^\s*END\s+CASE\b/i.test(code)) popExpect('CASE', i, 'END CASE')
		else if (/^\s*END\s+FOREACH\b/i.test(code)) popExpect('FOREACH', i, 'END FOREACH')
	}

	// 未闭合块报错
	for (const unclosed of stack) {
		const range = {
			start: { line: unclosed.line, character: 0 },
			end: { line: unclosed.line, character: Math.max(1, lines[unclosed.line]?.length ?? 1) }
		}
		diagnostics.push(createDiagnostic(document, DiagnosticSeverity.Error, range, `语法错误：缺少 ${unclosed.type} 的结束语句`))
	}

	return diagnostics
}

/**
 * 行内容检查函数
 * 对单行代码进行各种验证
 * @param document 文档对象
 * @param line 行内容
 * @param lineNumber 行号
 * @returns 诊断信息数组
 */
export const checkLine = (
	document: TextDocument,
	line: string,
	lineNumber: number
): Diagnostic[] => {
	// 快速跳过空行
	if (!line.trim()) return []
	
	// 跳过注释行
	if (REGEX_PATTERNS.COMMENT.test(line.trimStart())) return []
	
	// 跳过包含字符串的行（避免误报）
	if (REGEX_PATTERNS.STRING.test(line)) return []

	const diagnostics: Diagnostic[] = []

	// 检查小写字符（关键字大小写检查）
	if (REGEX_PATTERNS.LOWERCASE_WORD.test(line)) {
		const needUppercaseKeys = checkKeywordCase(line)
		needUppercaseKeys.forEach(key => {
			const character = line.indexOf(key)
			const range = {
				start: { line: lineNumber, character },
				end: { line: lineNumber, character: character + key.length }
			}
			diagnostics.push(
				createDiagnostic(document, DiagnosticSeverity.Warning, range, DIAGNOSTIC_MESSAGES.KEYWORD_CASE_ERROR)
			)
		})
	}

	// 检查 CALL 语句结尾
	if (checkStatementEnd(line)) {
		const range = {
			start: { line: lineNumber, character: line.length },
			end: { line: lineNumber, character: line.length }
		}
		diagnostics.push(
			createDiagnostic(document, DiagnosticSeverity.Error, range, DIAGNOSTIC_MESSAGES.STATEMENT_END_ERROR)
		)
	}

	return diagnostics
}

/**
 * 文档验证主函数
 * 对整个文档进行语法检查
 * @param document 要验证的文档
 * @returns 诊断信息数组的Promise
 */
export const validateTextDocument = async (document: TextDocument): Promise<Diagnostic[]> => {
	try {
		const text = document.getText()
		const lines = text.split(/\r?\n/)

		// 行级检查
		const lineDiags = lines.map((line, idx) => checkLine(document, line, idx)).flat()

		// 结构化检查（整文件一次）
		const structDiags = checkStructureDiagnostics(document, lines)

		// 合并并过滤
		return [...lineDiags, ...structDiags].filter(Boolean)
	} catch (error) {
		console.error('文档验证失败:', error)
		return []
	}
}